/**
 * 请求拦截器
 * 处理请求时的token验证和自动刷新功能
 */
import { getDeviceId } from './device.js';
import { 
  getToken, 
  setToken, 
  clearToken, 
  getTokenData, 
  isTokenExpired, 
  isTokenExpiringSoon, 
  isRefreshingGlobally, 
  acquireGlobalRefreshLock, 
  releaseGlobalRefreshLock,
  getLastRefreshTime,
  setLastRefreshTime,
  canRefreshToken
} from './tokenManager.js';

// 请求队列 - 存储等待token刷新完成的请求
let waitingQueue = [];
// 最小刷新间隔(毫秒)，避免频繁刷新
const MIN_REFRESH_INTERVAL = 30 * 1000; // 30秒

/**
 * 获取存储的用户ID
 * @returns {String} 用户ID
 */
function getStoredUserId() {
  try {
    const userInfoStr = uni.getStorageSync('userInfo');
    if (userInfoStr) {
      const userInfo = JSON.parse(userInfoStr);
      return userInfo.userId || userInfo._id || '';
    }
  } catch (e) {
    console.error('获取用户ID失败', e);
  }
  return '';
}

/**
 * 重定向到登录页面
 */
function resetToLogin() {
  // 清除登录信息但保留历史token
  clearToken(true);
  // 跳转到登录页
  uni.navigateTo({
    url: '/pages/login/login'
  });
}

/**
 * 刷新token
 * @param {String} token 当前token
 * @returns {Promise} 刷新结果
 */
async function refreshToken(token = '') {
  // 使用传入的token或者获取当前token
  const currentToken = token || getToken();
  const userId = getStoredUserId();
  const deviceId = getDeviceId();
  
  if (!currentToken) {
    return Promise.reject(new Error('无token可刷新'));
  }
  
  console.log('[刷新Token] 开始调用刷新云函数');
  
  // 调用云函数刷新token
  try {
    const res = await uniCloud.callFunction({
      name: 'refreshToken',
      data: { 
        token: currentToken,
        userId: userId,
        device_id: deviceId
      }
    });
    
    console.log('[刷新Token] 云函数返回结果', res.result);
    
    if (res.result.code === 0 && res.result.token) {
      // 保存新token
      setToken(res.result.token, res.result.tokenExpired);
      // 记录刷新时间
      setLastRefreshTime();
      return {
        token: res.result.token,
        tokenExpired: res.result.tokenExpired
      };
    } else {
      throw new Error(res.result.message || 'token刷新失败');
    }
  } catch (error) {
    console.error('[刷新Token] 调用失败:', error);
    throw error;
  }
}

/**
 * 处理token刷新
 * @returns {Promise<string>} 刷新后的token或null
 */
async function handleTokenRefresh() {
  // 检查全局刷新状态
  if (isRefreshingGlobally()) {
    console.log('[请求] 检测到其他页面正在刷新token，加入等待队列');
    return new Promise((resolve) => {
      waitingQueue.push(resolve);
      
      // 设置超时处理，防止长时间等待
      setTimeout(() => {
        const index = waitingQueue.indexOf(resolve);
        if (index !== -1) {
          waitingQueue.splice(index, 1);
          resolve(getToken());
        }
      }, 5000); // 等待最多5秒
    });
  }
  
  // 检查最小刷新间隔
  if (!canRefreshToken()) {
    console.log('[请求] 由于刷新间隔过短，跳过token刷新');
    return getToken();
  }
  
  // 尝试获取全局刷新锁
  if (!acquireGlobalRefreshLock()) {
    console.log('[请求] 无法获取全局刷新锁，可能其他页面正在刷新');
    return new Promise((resolve) => {
      waitingQueue.push(resolve);
      
      // 设置超时处理
      setTimeout(() => {
        const index = waitingQueue.indexOf(resolve);
        if (index !== -1) {
          waitingQueue.splice(index, 1);
          resolve(getToken());
        }
      }, 5000);
    });
  }
  
  console.log('[请求] 成功获取刷新锁，开始刷新token');
  
  try {
    // 执行token刷新
    const result = await refreshToken();
    console.log('[请求] token刷新成功');
    
    // 获取新token
    const newToken = result.token;
    
    // 通知所有等待的请求
    notifyWaitingRequests(newToken);
    
    return newToken;
  } catch (error) {
    console.error('[请求] token刷新失败', error);
    
    // 尝试使用历史token
    const historyToken = getToken(true);
    if (historyToken) {
      console.log('[请求] 使用历史token继续请求');
      notifyWaitingRequests(historyToken);
      return historyToken;
    }
    
    // 通知所有等待的请求刷新失败
    notifyWaitingRequests(null);
    
    // 跳转到登录页
    resetToLogin();
    return null;
  } finally {
    // 释放全局刷新锁
    releaseGlobalRefreshLock();
  }
}

/**
 * 通知所有等待的请求
 * @param {String} token 刷新后的token
 */
function notifyWaitingRequests(token) {
  if (waitingQueue.length === 0) return;
  
  console.log(`[请求] 通知${waitingQueue.length}个等待的请求`);
  
  // 复制队列并清空原队列
  const queue = [...waitingQueue];
  waitingQueue = [];
  
  // 逐个通知
  for (const resolve of queue) {
    resolve(token);
  }
}

/**
 * 处理响应错误
 * @param {Object} response 响应对象
 * @param {Object} config 请求配置
 * @returns {Object} 处理后的错误对象
 */
function handleResponseError(response, config) {
  // 如果响应为空或没有错误信息，返回默认错误
  if (!response) {
    return {
      code: -1,
      message: '网络错误，请检查网络连接'
    };
  }
  
  // 处理token相关错误
  if (response.code === 401 || response.code === 40101 || response.code === 30203) {
    // 如果是token过期，尝试刷新
    if (response.code === 30203) {
      console.log('Token已过期，尝试刷新');
      
      // 异步刷新token，不影响当前请求的错误处理
      handleTokenRefresh().then(newToken => {
        if (!newToken) {
          // 刷新失败，重定向到登录页
          resetToLogin();
        }
      }).catch(() => {
        resetToLogin();
      });
    } else {
      // 其他授权错误，直接重定向到登录页
      console.log('授权失败，需要重新登录');
      resetToLogin();
    }
  }
  
  // 返回原始错误
  return response;
}

/**
 * 请求拦截器
 * @param {Object} config 请求配置
 * @returns {Object} 处理后的请求配置
 */
async function requestInterceptor(config) {
  // 检查token是否已经完全过期
  if (isTokenExpired()) {
    console.log('[Token] Token已过期，尝试刷新');
    const newToken = await handleTokenRefresh();
    if (newToken) {
      console.log('[Token] Token已刷新，继续请求');
    } else {
      console.log('[Token] Token刷新失败，尝试使用现有token继续请求');
    }
  } 
  // 检查token是否即将过期，但仅当剩余时间很短（小于10分钟）时才刷新
  // 这样可以避免在每次检测到即将过期（2小时内）就立即刷新
  else if (isTokenExpiringSoon() && canRefreshToken()) {
    // 获取过期时间与当前时间的差值，单位分钟
    const expireTime = getTokenExpireTime();
    const currentTime = Date.now();
    const remainingMinutes = Math.floor((expireTime - currentTime) / (60 * 1000));
    
    // 仅当剩余时间小于10分钟时，才执行刷新操作
    if (remainingMinutes < 10) {
      console.log(`[Token] Token将在${remainingMinutes}分钟后过期，执行提前刷新`);
      const newToken = await handleTokenRefresh();
      if (newToken) {
        console.log('[Token] Token已刷新，继续请求');
      } else {
        console.log('[Token] Token刷新失败，尝试使用现有token继续请求');
      }
    } else {
      console.log(`[Token] Token将在${remainingMinutes}分钟后过期，暂不刷新`);
    }
  }
  
  // 获取最新token（可能是刷新后的新token或历史token）
  const token = getToken();
  
  // 如果有token，添加到请求头
  if (token) {
    if (!config.header) {
      config.header = {};
    }
    config.header['Authorization'] = token;
    config.header['token'] = token;
    config.header['device-id'] = getDeviceId();
  }
  
  return config;
}

/**
 * 统一请求方法
 * @param {Object} options 请求配置
 * @returns {Promise} 请求结果
 */
export const request = async (options) => {
  // 应用请求拦截器进行token检查和刷新
  const config = await requestInterceptor({
    ...options,
    header: options.header || {}
  });
  
  return new Promise((resolve, reject) => {
    uni.request({
      url: config.url,
      method: config.method || 'GET',
      data: config.data || {},
      header: config.header,
      success: async (res) => {
        // 处理token过期
        if (res.data.code === -1 && res.data.message && res.data.message.includes('token')) {
          // 如果是token相关错误
          if (options.ignoreToken) {
            // 如果配置了忽略token验证，直接返回结果
            resolve(res.data);
          } else {
            console.log('Token无效或已过期');
            
            // 尝试刷新token
            const newToken = await handleTokenRefresh();
            
            if (newToken) {
              // 使用新token重试请求
              console.log('使用新token重试请求');
              
              // 更新请求配置中的token
              config.header['Authorization'] = newToken;
              config.header['token'] = newToken;
              
              // 重新发起请求
              uni.request({
                ...config,
                success: (retryRes) => {
                  resolve(retryRes.data);
                },
                fail: (error) => {
                  console.error('重试请求失败:', error);
                  reject(handleResponseError(null, config));
                }
              });
              return;
            }
            
            // 如果刷新失败，尝试清除token并跳转登录页
            resetToLogin();
            reject(res.data);
          }
        } else {
          // 正常请求返回
          resolve(res.data);
        }
      },
      fail: (err) => {
        reject(handleResponseError(null, config));
      }
    });
  });
};

/**
 * 云函数请求方法
 * @param {Object} options 请求配置
 * @returns {Promise} 请求结果
 */
export const callFunction = (options) => {
  // 获取token和设备ID
  const tokenData = getTokenData();
  const token = tokenData.token;
  const deviceId = tokenData.device_id;
  const userId = getStoredUserId();
  
  // 如果需要验证token且没有token，则跳转到登录页
  if (options.needToken && !token) {
    console.log('未登录，跳转到登录页');
    uni.navigateTo({
      url: '/pages/login/login'
    });
    return Promise.reject({ code: -1, message: '未登录' });
  }
  
  // 如果在刷新token且不是刷新token的请求，将请求加入队列
  if (isRefreshingGlobally() && options.name !== 'refreshToken' && options.data?.action !== 'refreshToken') {
    return new Promise((resolve, reject) => {
      waitingQueue.push((newToken) => {
        if (newToken) {
          // 更新请求中的token
          const newOptions = { ...options };
          if (!newOptions.data) newOptions.data = {};
          newOptions.data.token = newToken;
          newOptions.data.device_id = deviceId;
          
          // 使用新token重试请求
          callFunction(newOptions).then(resolve).catch(reject);
        } else {
          reject(new Error('Token刷新失败'));
        }
      });
    });
  }
  
  // 添加token等信息到请求数据
  const data = {
    ...options.data
  };
  
  if (token && !options.ignoreToken) {
    data.token = token;
    data.device_id = deviceId;
    if (userId) {
      data.userId = userId;
    }
  }
  
  return new Promise((resolve, reject) => {
    uniCloud.callFunction({
      name: options.name,
      data: data,
      success: async (res) => {
        // 处理结果
        const result = res.result;
        
        // 如果token无效或过期
        if (result.code === -1 && result.message && result.message.includes('token')) {
          if (options.ignoreToken) {
            // 如果配置了忽略token验证，直接返回结果
            resolve(result);
          } else {
            console.log('Token无效或已过期');
            
            // 尝试刷新token
            const newToken = await handleTokenRefresh();
            
            if (newToken) {
              console.log('使用新token重试请求');
              
              // 使用新token重新发起当前请求
              const newOptions = { ...options };
              if (!newOptions.data) newOptions.data = {};
              newOptions.data.token = newToken;
              newOptions.data.device_id = deviceId;
              
              uniCloud.callFunction({
                name: newOptions.name,
                data: newOptions.data,
                success: (newRes) => {
                  resolve(newRes.result);
                },
                fail: (err) => {
                  reject(err);
                }
              });
              return;
            }
            
            // 如果刷新失败，尝试清除token并跳转登录页
            resetToLogin();
            reject(result);
          }
        } 
        // 如果需要刷新token
        else if (result.code === 0 && result.data?.need_refresh && !isRefreshingGlobally()) {
          console.log('Token需要刷新');
          
          // 直接调用token刷新处理器
          const newToken = await handleTokenRefresh();
          
          if (newToken) {
            console.log('使用新token重试请求');
            
            // 使用新token重新发起当前请求
            const newOptions = { ...options };
            if (!newOptions.data) newOptions.data = {};
            newOptions.data.token = newToken;
            newOptions.data.device_id = deviceId;
            
            uniCloud.callFunction({
              name: newOptions.name,
              data: newOptions.data,
              success: (newRes) => {
                resolve(newRes.result);
              },
              fail: (err) => {
                reject(err);
              }
            });
          } else {
            // 正常返回结果
            resolve(result);
          }
        } else {
          // 正常返回结果
          resolve(result);
        }
      },
      fail: (err) => {
        reject(err);
      }
    });
  });
};

/**
 * 初始化token刷新机制
 * 应在应用启动时调用
 */
export function initTokenRefresher() {
  console.log('[Token] 初始化Token刷新机制');
  
  // 清理过期的刷新锁（如果有的话）
  if (isRefreshingGlobally()) {
    const lockInfo = uni.getStorageSync('global_token_refresh_lock');
    if (lockInfo) {
      try {
        const lockData = JSON.parse(lockInfo);
        const now = Date.now();
        // 如果锁已存在超过1分钟，则认为是上次应用异常退出造成的，直接释放
        if (now - lockData.timestamp > 60 * 1000) {
          console.log('[Token] 检测到可能是异常退出导致的刷新锁，自动释放');
          releaseGlobalRefreshLock();
        }
      } catch (e) {
        console.error('[Token] 解析刷新锁信息失败', e);
        releaseGlobalRefreshLock();
      }
    }
  }
  
  // 延迟检查token状态
  setTimeout(() => {
    // 检查token是否存在
    const token = getToken();
    if (!token) {
      console.log('[Token] 无token，无需刷新');
      return;
    }
    
    // 检查token是否已过期
    if (isTokenExpired()) {
      console.log('[Token] Token已过期，尝试刷新');
      handleTokenRefresh().catch(err => {
        console.error('[Token] 启动时刷新token失败', err);
      });
    } 
    // 检查token是否即将过期，但仅当剩余时间很短（小于10分钟）时才刷新
    else if (isTokenExpiringSoon() && canRefreshToken()) {
      // 获取过期时间与当前时间的差值，单位分钟
      const expireTime = getTokenExpireTime();
      const currentTime = Date.now();
      const remainingMinutes = Math.floor((expireTime - currentTime) / (60 * 1000));
      
      // 仅当剩余时间小于10分钟时，才执行刷新操作
      if (remainingMinutes < 10) {
        console.log(`[Token] Token将在${remainingMinutes}分钟后过期，执行提前刷新`);
        handleTokenRefresh().catch(err => {
          console.error('[Token] 启动时刷新token失败', err);
        });
      } else {
        console.log(`[Token] Token将在${remainingMinutes}分钟后过期，暂不刷新`);
      }
    } else {
      console.log('[Token] Token有效，无需刷新');
    }
  }, 1000); // 延迟1秒，等待应用完全加载
} 